added some development tools
[windows-sources.git] / developer / Samples / NET 4.6 / Samples for Parallel / VisualizePartitioning / VisualizePartitioning_CSharp / MainForm.cs
blob1b9eaa2d9d3aebcc7944da23e9c90d8b95d1ddaa
1 //--------------------------------------------------------------------------
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // File: MainForm.cs
6 //
7 //--------------------------------------------------------------------------
9 using System;
10 using System.Collections.Concurrent;
11 using System.Collections.Concurrent.Partitioners;
12 using System.Collections.Generic;
13 using System.Diagnostics;
14 using System.Drawing;
15 using System.Linq;
16 using System.Runtime.CompilerServices;
17 using System.Threading;
18 using System.Threading.Tasks;
19 using System.Windows.Forms;
20 using Microsoft.Drawing;
22 namespace VisualizePartitioning
24 public partial class MainForm : Form
26 /// <summary>Name and identifier for turning on stripe partitioning with PLINQ.</summary>
27 const string PartitioningStripe = "Stripe";
28 /// <summary>Name and identifier for turning on hash partitioning with PLINQ.</summary>
29 const string PartitioningHash = "Hash";
31 /// <summary>Color palette to use when rendering threads.</summary>
32 private Color[] _colors;
33 /// <summary>A multiplicative factor of how much work to do for each rendered line.</summary>
34 private int _workFactor;
36 /// <summary>Provides a source of seeds for thread-local random instances.</summary>
37 private static Random _randomnessSeed = new Random();
38 /// <summary>A thread-safe source of randomness for all threads that need random values.</summary>
39 private static ThreadLocal<Random> _localRandom = new ThreadLocal<Random>(delegate { lock (_randomnessSeed) return new Random(_randomnessSeed.Next()); });
41 public MainForm()
43 InitializeComponent();
45 // Configure the workloads and the color palette. The partitioning methods initialization will be done
46 // when the radio button is changed to Parallel.ForEach or PLINQ. The color palette will be
47 // initialized when the cores trackbar changes value.
48 InitializeWorkloads();
50 // Configure number of cores
51 tbCores.Minimum = 1;
52 tbCores.Maximum = Environment.ProcessorCount * 2;
53 tbCores.Value = Environment.ProcessorCount;
56 /// <summary>Initializes the color palette to use when rendering threads.</summary>
57 private void InitializeColorPalette()
59 Random random = new Random(8); // Change seed value to change the palette used
60 _colors = (from i in Enumerable.Range(0, tbCores.Value)
61 select Color.FromArgb(random.Next(128) + 127, random.Next(128) + 127, random.Next(128) + 127)).ToArray();
64 /// <summary>Initializes the workloads list view.</summary>
65 private void InitializeWorkloads()
67 lvWorkloads.Items.Clear();
68 var workloads = new List<Tuple<string, Func<int, int, int>>>();
70 // NOTE: To add a new workload, simply add a new entry below with a name and corresponding function
71 workloads.Add(Tuple.Create<string, Func<int, int, int>>("Constant", (size, current) => 1000 * _workFactor));
72 workloads.Add(Tuple.Create<string, Func<int, int, int>>("Increasing Linear", (size, current) => 200 * current * _workFactor));
73 workloads.Add(Tuple.Create<string, Func<int, int, int>>("Decreasing Linear", (size, current) => 200 * (size - current) * _workFactor));
74 workloads.Add(Tuple.Create<string, Func<int, int, int>>("Random", (size, current) => _localRandom.Value.Next(100, 10000) * _workFactor));
76 foreach (var workload in workloads) lvWorkloads.Items.Add(new ListViewItem(workload.Item1) { Tag = workload });
77 lvWorkloads.Items[0].Selected = true;
80 /// <summary>Initializes the partitioning methods list view.</summary>
81 private void InitializePartitioningMethods()
83 lvPartitioningMethods.Items.Clear();
84 bool usingPLINQ = rbPLINQ.Checked;
85 var partitioningMethods = new List<Tuple<string, Func<int[], Partitioner<int>>>>();
87 // Static partitioning using the Partitioner.Create overload requires static partitioner support,
88 // which Parallel.ForEach does not provide.
89 if (usingPLINQ)
91 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
92 "Static", e => Partitioner.Create(e, false)));
95 // Add a bunch of partitioning approaches that work with both PLINQ and Parallel.ForEach
96 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
97 "Load Balance", e => Partitioner.Create(e, true)));
98 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
99 "Dynamic(1)", e => ChunkPartitioner.Create(e, 1)));
100 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
101 "Dynamic(16)", e => ChunkPartitioner.Create(e, 16)));
102 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
103 "Guided", e => ChunkPartitioner.Create(e, prev =>
105 if (prev <= 0) return e.Length <= 1 ? 1 : e.Length / (Environment.ProcessorCount * 3);
106 var next = prev / 2;
107 return next <= 0 ? prev : next;
108 })));
109 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
110 "Grow Exponential", e => ChunkPartitioner.Create(e, prev => prev <= 0 ? 1 : prev * 2)));
111 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
112 "Random", e => ChunkPartitioner.Create(e, prev => _localRandom.Value.Next(e.Length))));
114 // Special-case some PLINQ-only hashing
115 if (usingPLINQ)
117 // The actual enabling of these partitioning schemes is done later, as they can't
118 // be encoded in a partitioner but rather are based on what operators are used in the PLINQ query.
119 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
120 PartitioningStripe, e => Partitioner.Create(e)));
121 partitioningMethods.Add(Tuple.Create<string, Func<int[], Partitioner<int>>>(
122 PartitioningHash, e => Partitioner.Create(e)));
125 // Dump the partitioners into the list view
126 foreach (var method in partitioningMethods) lvPartitioningMethods.Items.Add(new ListViewItem(method.Item1) { Tag = method });
127 lvPartitioningMethods.Items[0].Selected = true;
130 /// <summary>Visualize the partitioning.</summary>
131 private void btnVisualize_Click(object sender, EventArgs e)
133 int numProcs = tbCores.Value;
134 int width = pbPartitionedImage.Width, height = pbPartitionedImage.Height;
135 bool useParallelFor = rbParallelFor.Checked, useParallelForEach = rbParallelForEach.Checked;
136 _workFactor = tbWorkFactor.Value;
138 // If we're using Parallel.ForEach or PLINQ, ensure a partitioning scheme was selected and use it
139 Tuple<string, Func<int[], Partitioner<int>>> selectedMethod = null;
140 if (!useParallelFor)
142 if (lvPartitioningMethods.SelectedIndices.Count == 0) return;
143 else selectedMethod = (Tuple<string, Func<int[], Partitioner<int>>>)lvPartitioningMethods.SelectedItems[0].Tag;
146 // Make sure a workload was selected and use it
147 if (lvWorkloads.SelectedItems.Count == 0) return;
148 var selectedWorkload = (Tuple<string, Func<int, int, int>>)lvWorkloads.SelectedItems[0].Tag;
150 // Create a new Bitmap to store the rendered output
151 var bmp = new Bitmap(width, height);
153 // Disable the start button and kick off the background work
154 btnVisualize.Enabled = false;
155 Task.Factory.StartNew(() =>
157 int nextId = -1; // assign each thread a unique id
158 var threadId = new ThreadLocal<int>(() => Interlocked.Increment(ref nextId));
160 using (FastBitmap fastBmp = new FastBitmap(bmp)) // get faster access to the Bitmap's contents
162 var sw = Stopwatch.StartNew(); // time the operation
163 if (useParallelFor)
165 Parallel.For(0, height, new ParallelOptions { MaxDegreeOfParallelism = numProcs }, i =>
167 int id = threadId.Value;
168 DoWork(selectedWorkload.Item2(height, i));
169 for (int j = 0; j < width; j++) fastBmp.SetColor(j, i, _colors[id % _colors.Length]);
172 else
174 // Create the partitioner to be used
175 var partitioner = selectedMethod.Item2(Enumerable.Range(0, height).ToArray());
177 if (useParallelForEach)
179 // Run the work with Parallel.ForEach
180 Parallel.ForEach(partitioner, new ParallelOptions { MaxDegreeOfParallelism = numProcs }, i =>
182 int id = threadId.Value;
183 DoWork(selectedWorkload.Item2(height, i));
184 for (int j = 0; j < width; j++) fastBmp.SetColor(j, i, _colors[id % _colors.Length]);
187 else // PLINQ
189 // Run the work with PLINQ. If a special partitioning method was selected, use relevant query operators
190 // to get PLINQ to use that partitioning approach.
191 var source = partitioner.AsParallel().WithDegreeOfParallelism(numProcs);
192 if (selectedMethod.Item1 == PartitioningStripe) source = source.TakeWhile(elem => true);
193 else if (selectedMethod.Item1 == PartitioningHash) source = source.Join(Enumerable.Range(0, height).AsParallel(), i => i, i => i, (i, ignore) => i);
194 source.ForAll(i =>
196 int id = threadId.Value;
197 DoWork(selectedWorkload.Item2(height, i));
198 for (int j = 0; j < width; j++) fastBmp.SetColor(j, i, _colors[id % _colors.Length]);
203 // Return the total time from the task
204 return sw.Elapsed;
207 // When the work completes, run the following on the UI thread
208 }).ContinueWith(t =>
210 // Dispose of the old image (if there was one) and display the new one
211 var old = pbPartitionedImage.Image;
212 pbPartitionedImage.Image = bmp;
213 if (old != null) old.Dispose();
215 // Re-enable controls on the form and display the elapsed time
216 btnVisualize.Enabled = true;
217 lblTime.Text = "Time: " + t.Result.ToString();
218 }, TaskScheduler.FromCurrentSynchronizationContext());
221 /// <summary>Does an amount of work relative to the amount requested.</summary>
222 /// <param name="workAmount">The amount of work to perform.</param>
223 [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
224 private static int DoWork(int workAmount)
226 int value = 1;
227 for (int i = 0; i < workAmount; i++) value *= workAmount;
228 return value;
231 /// <summary>Update relevant portions of the form when the API radio buttons are checked.</summary>
232 /// <param name="sender">The radio button.</param>
233 /// <param name="e">The event args.</param>
234 private void rbAPI_CheckedChanged(object sender, EventArgs e)
236 lvPartitioningMethods.Enabled = !rbParallelFor.Checked;
237 lvPartitioningMethods.HideSelection = !lvPartitioningMethods.Enabled;
239 // Recreate partitioning methods every time a radio button is checked,
240 // as which API is selected determines which partitioning methods are available
241 InitializePartitioningMethods();
244 private void tbCores_ValueChanged(object sender, EventArgs e)
246 toolTip1.SetToolTip(tbCores, tbCores.Value.ToString());
247 InitializeColorPalette();
248 int worker, io;
249 ThreadPool.GetMinThreads(out worker, out io);
250 ThreadPool.SetMinThreads(tbCores.Value, io);
253 private void tbWorkFactor_ValueChanged(object sender, EventArgs e)
255 toolTip1.SetToolTip(tbWorkFactor, tbWorkFactor.Value.ToString());